package com.ziytek.web.citizen.util;

import com.ziytek.web.citizen.caffeine.CachePutOrGet;
import com.ziytek.web.citizen.config.caffeine.CaffeineUtil;
import com.ziytek.web.citizen.constant.NumConstant;
import com.ziytek.web.citizen.constant.ServerConstant;
import com.ziytek.web.citizen.enums.ErrorTypeEnum;
import com.ziytek.web.citizen.enums.StepTypeNoEnum;
import com.ziytek.web.citizen.model.bean.CapStatsVo;
import com.ziytek.web.citizen.model.bean.DischargeCapacity;
import com.ziytek.web.citizen.model.bean.DischargeData;
import com.ziytek.web.citizen.service.AdaptiveSimpsonService;
import com.ziytek.web.citizen.service.BatteryCapacityService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.LinkedList;
import java.util.stream.Collectors;

/**
 * @author fengh
 */
@Slf4j
public class BatteryCapacityCalculator {

    private static final CachePutOrGet CACHE_PUT_OR_GET = SpringBeanUtils.getApplicationContext().getBean(CachePutOrGet.class);
    private static final BatteryCapacityService SERVICE = SpringBeanUtils.getApplicationContext().getBean(BatteryCapacityService.class);
    private static final AdaptiveSimpsonService adaptiveSimpsonService = SpringBeanUtils.getApplicationContext().getBean(AdaptiveSimpsonService.class);

    private static boolean isCharge(String commandSetType) {
        return StepTypeNoEnum.STEP_TYPE_CCC_NUM.getCode().equalsIgnoreCase(commandSetType)
                || StepTypeNoEnum.STEP_TYPE_CC_NUM.getCode().equalsIgnoreCase(commandSetType)
                || StepTypeNoEnum.STEP_TYPE_CV_NUM.getCode().equalsIgnoreCase(commandSetType)
                || StepTypeNoEnum.STEP_TYPE_CPC_NUM.getCode().equalsIgnoreCase(commandSetType);
    }

    private static boolean isDisCharge(String commandSetType) {
        return StepTypeNoEnum.STEP_TYPE_CCD_NUM.getCode().equalsIgnoreCase(commandSetType)
                || StepTypeNoEnum.STEP_TYPE_CPD_NUM.getCode().equalsIgnoreCase(commandSetType)
                || StepTypeNoEnum.STEP_TYPE_CRD_NUM.getCode().equalsIgnoreCase(commandSetType);
    }

    /**
     * Description: 放电结束判断
     *
     * @param c 电流
     * @param v 电压
     * @return 异常数据类型
     */
    public static int errorData(Float c, Float v) {
        // 电池空闲 电压小于2000mV,判断为空闲状态
        if (v != null && Math.abs(v) >= NumConstant.FLOAT_ZER && Math.abs(v) <= NumConstant.FLOAT_TWO_THOUSAND) {
            return ErrorTypeEnum.LEISURE.getCode();
        }

        // 从控板通讯异常
        if (v != null && Math.abs(v) == NumConstant.FLOAT_ZER) {
            return ErrorTypeEnum.CONTROL_COMMUNICATION_ERROR.getCode();
        }

        // 电池反接(5V30A里正值代表充电,负值代表放电，不能再判断反接情况，会与放电冲突)
//        if (v != null && v < NumConstant.FLOAT_ZER && Math.abs(v) >= NumConstant.FLOAT_ONE_THOUSAND) {
//            return ErrorTypeEnum.REVERSE_BATTERY.getCode();
//        }

        // 电池限制
        boolean limit = v != null && v > NumConstant.FLOAT_ONE_THOUSAND && Math.abs(v) == NumConstant.FLOAT_BATTERY_LIMIT
                && c != null && c > NumConstant.FLOAT_ONE_THOUSAND && Math.abs(c) == NumConstant.FLOAT_BATTERY_LIMIT;
        if (limit) {
            return ErrorTypeEnum.BATTERY_LIMIT.getCode();
        }

        // PWM失控
        boolean pwmError = v != null && v > NumConstant.FLOAT_ONE_THOUSAND && Math.abs(v) == NumConstant.FLOAT_PWM_ERROR
                && c != null && c > NumConstant.FLOAT_ONE_THOUSAND && Math.abs(c) == NumConstant.FLOAT_PWM_ERROR;
        if (pwmError) {
            return ErrorTypeEnum.PWM_ERROR.getCode();
        }

        // 设备自检异常
        boolean checkError = v != null && v > NumConstant.FLOAT_ONE_THOUSAND && Math.abs(v) == NumConstant.FLOAT_CHECK_ERROR
                && c != null && c > NumConstant.FLOAT_ONE_THOUSAND && Math.abs(c) == NumConstant.FLOAT_CHECK_ERROR;
        if (checkError) {
            return ErrorTypeEnum.CHECK_ERROR.getCode();
        }

        return ErrorTypeEnum.EXECUTING.getCode();
    }

    /**
     * Description: 放电结束判断
     *
     * @param key key
     * @param c   电流
     * @param v   电压
     * @return true
     */
    public static int dischargeFinished(String key, Float c, Float v, Float cutOff) {
        // 异常数据
        int errorFlag = errorData(c, v);
        if (ErrorTypeEnum.EXECUTING.getCode() != errorFlag) {
            return errorFlag;
        }

        // 放电结束,判断条件:(电压小于等于截止电压)
        boolean finished = v != null && Math.abs(v) <= cutOff;
        boolean finishedTwo = c != null && Math.abs(c) <= 50F;
        if (finished || finishedTwo) {
            log.info("放电结束标识,key:{},v:{},cutOff:{}", key, v, cutOff);
            return ErrorTypeEnum.EXECUTION_COMPLETED.getCode();
        }

        return ErrorTypeEnum.EXECUTING.getCode();
    }


    /**
     * Description: 充电结束判断
     *
     * @param key    key
     * @param c      电流
     * @param v      电压
     * @param cutOff 截止电压或电流
     * @return true
     */
    public static int chargeFinished(String key, Float c, Float v, Float cutOff) {
        // 异常数据
        int errorFlag = errorData(c, v);
        if (ErrorTypeEnum.EXECUTING.getCode() != errorFlag) {
            return errorFlag;
        }

        // 充电结束,判断条件:(电流小于等于截止电流)
        boolean finished = c != null && Math.abs(c) <= cutOff;
        boolean finishedTwo = c != null && Math.abs(c) <= 50F;
        if (finished || finishedTwo) {
            log.info("充电结束标识key:{},c:{},cutOff:{}", key, c, cutOff);
            return ErrorTypeEnum.EXECUTION_COMPLETED.getCode();
        }

        return ErrorTypeEnum.EXECUTING.getCode();
    }

    /**
     * 计算统计数据
     *
     * @param key     key
     * @param current 电流数组
     * @return 统计结果对象
     * @throws IllegalArgumentException 如果电流数据无效
     */
    public static CapStatsVo calculateStats(String commandSetType, CapStatsVo stats, String key, LinkedList<DischargeData> current, int cv, int vv, int cutOff) {
        if (stats == null) {
            stats = new CapStatsVo();
            stats.setTs(0D);
        }

        // 取绝对值
        var c = Math.abs(cv);
        var v = Math.abs(vv);
        stats.setIv(c, v);
        stats.setK(key);
        stats.setT(ErrorTypeEnum.EXECUTING.getCode());
        stats.setEt(ServerConstant.ERROR_EXCEPTION_TYPE_NORMAL);

        if (isDisCharge(commandSetType)) {
            int f = dischargeFinished(key, c * NumConstant.FLOAT_ONE, v * NumConstant.FLOAT_ONE, cutOff * 1F);
            stats.setT(f);
        }

        if (isCharge(commandSetType)) {
            int f = chargeFinished(key, c * NumConstant.FLOAT_ONE, v * NumConstant.FLOAT_ONE, cutOff * 1F);
            stats.setT(f);
        }

        //log.info("容量计算key:{}-状态:{}", key, stats.t);
        // 是否是异常数据
        stats.setEt(stats.getT() == ErrorTypeEnum.EXECUTION_COMPLETED.getCode() ||
                stats.getT() == ErrorTypeEnum.EXECUTING.getCode() ?
                ServerConstant.ERROR_EXCEPTION_TYPE_NORMAL : ServerConstant.ERROR_EXCEPTION_TYPE_ERROR);

        // 正常数据
        if (ServerConstant.ERROR_EXCEPTION_TYPE_NORMAL == stats.getEt() && Math.abs(c) > NumConstant.INT_FIFTY) {
            var data = new DischargeData();
            data.setTimestamp(LocalDateTime.now());
            data.setCurrent(c);
            current.add(data);
        }

        // 正常数据(指令下发成功后，前面几帧数据电流数据值很小)
        if (stats.getTs() <= NumConstant.DOUBLE_THIRTY && ServerConstant.ERROR_EXCEPTION_TYPE_NORMAL == stats.getEt() && Math.abs(c) <= NumConstant.INT_FIFTY) {
            LinkedList<DischargeData> first = CACHE_PUT_OR_GET.getFirstCurrentCache(key);
            if (CollectionUtils.isEmpty(first)) {
                first = new LinkedList<>();
            }
            DischargeData data = new DischargeData();
            data.setTimestamp(LocalDateTime.now());
            data.setCurrent(c);
            first.add(data);
            CACHE_PUT_OR_GET.putFirstCurrentCache(key, first);
        }

        // 平均电流
        stats.setAc(current.stream().collect(Collectors.averagingDouble(DischargeData::getCurrent)));

        // 充电容量
        //double[] currentArray = current.stream().mapToDouble(DischargeData::getCurrent).toArray();
        // stats.c = calculateCapacity(currentArray, interval);
        //DischargeCapacity dischargeCapacity = adaptiveSimpsonService.adaptiveCalculate(current);
        DischargeCapacity dischargeCapacity = SERVICE.calculateCapacityTwo(current);

        if (dischargeCapacity != null && dischargeCapacity.getTimestamp() > NumConstant.LONG_ZERO) {
            stats.setC(dischargeCapacity.getCapacity());
            stats.setTs(dischargeCapacity.getTimestamp());
        }
//        // 中断后首个上送数据计算
//        if (addInterrupt && !CollectionUtils.isEmpty(current) && current.size() == NumConstant.INT_ONE) {
//            stats.setC(NumConstant.DOUBLE_ZERO);
//            stats.setTs(NumConstant.DOUBLE_ZERO);
//        }

        if (dischargeCapacity != null && dischargeCapacity.getTimestamp() <= NumConstant.LONG_ZERO) {
            LinkedList<DischargeData> first = CACHE_PUT_OR_GET.getFirstCurrentCache(key);
            if (!CollectionUtils.isEmpty(first)) {
                double time = first.getLast().getTimestamp().toEpochSecond(ZoneOffset.UTC) - first.getFirst().getTimestamp().toEpochSecond(ZoneOffset.UTC);
                stats.setTs(Math.abs((((Double) time)).longValue()));
            }
        }

        // 总充电时间小于50s的,不继续完成状态判断
        Integer firstDataCount = CaffeineUtil.get(CaffeineUtil.FIRST_DATA_LIST_KEY + key);
        if (stats.getTs() < NumConstant.DOUBLE_FIFTY && ErrorTypeEnum.EXECUTION_COMPLETED.getCode() == stats.getT() && firstDataCount != null && firstDataCount <= NumConstant.INT_FIVE) {
            log.info("小于50s容量计算key:{}-状态:{},时间:{}", key, stats.getT(), stats.getTs());
            stats.setT(ErrorTypeEnum.EXECUTING.getCode());
        }


        return stats;
    }
}